30

PHP 的redis扩展是阻塞式 IO ,使用订阅/发布模式时,会导致整个进程进入阻塞。因此必须使用Swoole\Redis异步客户端来实现。

实例代码

$server = new swoole_websocket_server("0.0.0.0", 9501);

$server->on('workerStart', function ($server, $workerId) {
    $client = new swoole_redis;
    $client->on('message', function (swoole_redis $client, $result) use ($server) {
        if ($result[0] == 'message') {
            foreach($server->connections as $fd) {
                $server->push($fd, $result[1]);
            }
        }
    });
    $client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) {
        $client->subscribe('msg_0');
    });
});

$server->on('open', function ($server, $request) {

});

$server->on('message', function (swoole_websocket_server $server, $frame) {
    $server->push($frame->fd, "hello");
});

$server->on('close', function ($serv, $fd) {

});

$server->start();

实现过程

  • 在进程启动(onWorkerStart)时创建了Swoole\Redis客户端,连接到Redis服务器

  • 连接成功后,订阅msg_0主题的消息

  • 当有新的message时,Swoole\Redis会触发onMessage事件回调

  • 在这个回调函数中使用$server->connections遍历服务器所有的连接,发送消息


韩天峰
7.9k 声望11.1k 粉丝

Swoole 开源项目创始人


25

引用和评论

25 条评论
头像
daydaygo

最后使用 process 解决的, 用一个单独的 process 来跑 redis sub

2017-10-26
zouliming

@daydaygo 用process来sub redis的可有源代码?另外看到很多人通过给worker编号,保证只有一个worker来订阅了redis,这样是不是就限制了吞吐量呢?

2018-01-08
daydaygo

@daydaygo @zouliming swoole 有2种worker, worker 配合 server, task 配置耗时任务投递, 不适合 pub/sub 这样的场景, 使用 Process 职责分明, 是最佳实践. 代码的话看你如何实现, 原生看 swoole wiki 就好了, 其他开源的框架都封装有自己的实现, 直接用就行, 比如 swoft

2018-01-08
zouliming

@daydaygo 谢谢回复。不过你好像没有回答我的问题,也可能是没有看懂我的问题吧。swoft的源码看了,可惜没找到process订阅redis这块。

2018-01-08
头像
jdxia

?,希望swoole越来越好

2017-09-04
头像
facades

这个类哪里来呀swoole_redis

2017-09-05
Yian

@facades 参考这个链接来编译安装 Swoole
https://www.swoole.com/wiki/p...

2017-10-07
头像
daydaygo

有 2 点疑问:

  1. 这里相当于把消息广播给每个 server, 然后 server 广播给了每个连接, 如果只想把消息广播给某个单独的连接呢?
  2. https://wiki.swoole.com/wiki/... 这个 wiki 中讲到多个进程不要公用一个连接, 所以在 workStart 回调中初始化, 这样的话, 这里是不是每个 worker 进程都会起一个 swoole_redis 来监听, 导致消息被重复广播?
2017-09-09
Yian

@daydaygo 像文中的代码那样把 Redis 的 on message 放在 Swoole 的 on workerStart 里,我这里每个页面会收到 8 条重复消息,然后我把 Redis 的 on message 放在 Swoole 的 on open 后就不会出现重复广播了...
初学 Swoole,还在慢慢摸索中...

2017-10-07
qqlcbb

@daydaygo @Yian 我按文中的写法,每个页面都会收到8条重复的消息。按你修改了就不会重复广播了,请问您已经找到原因了吗?

2017-10-26
daydaygo

@daydaygo @qqlcbb 可以去了解一下 swoole 的进程模型, onWorkerStart 是绑定到 worker 进程上, onOpen 是绑定到 master 进程开启上

2017-11-02
头像
jlzan1314

帮每个worker 订阅 编个号,发布订阅的时候,发给2个订阅不就解决重复的问题了

2017-11-28
头像
zouliming

韩总,demo中的实例代码,会订阅多次。如果只想订阅一次,应该如何写更好呢?

2018-01-08
易拉罐

@zouliming 可以new个swoole_process做订阅,然后server->addProcess 挂进去

2018-07-20
头像
since888

麻烦问下,之前这代码跑起来没问题,现在重装后,启动时候提示不能使用 new swoole_redis; 只让使用协程的new SwooleCoroutineRedis();
是啥情况

2018-12-22
since888

@since888 报错内容 SwooleRedis::__construct(): async APIs will be removed in Swoole-v4.3.0, you should be using the coroutine APIs instead

2018-12-22
羊爸爸

@since888 同样问题解决了吗?

2019-01-10
since888

@since888 @羊爸爸 新版本不支持这个,所以,吧版本降到1.8+就行

2019-01-10
头像
hxd_

请问一下我在 open 事件的时候能获取到userinfo, 然后需要根据其中的userid 去redis 查找 userid_messages 这样名称的队列,在哪里实现监听redis 队列的数据变动?workerstart 的时候没有userid

2019-02-20